﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace BloomFilter
{
    public abstract class BloomFilterManager<TBloomFilter, ValueType>
    {

        #region 文件操作部分字段

        object writeFileLock;
        /// <summary>
        /// 文件 写 处理缓存
        /// </summary>
        private byte[] fileWriteBuffer;


        object readFileLock;
        /// <summary>
        /// 文件 读 处理缓存
        /// </summary>
        private byte[] fileReadBuffer;

        #endregion

        #region 过滤器管理器字段

        /// <summary>
        /// 过滤器组操作锁
        /// </summary>
        private object bloomFiltersLock;

        /// <summary>
        /// 过滤器组
        /// </summary>
        protected Dictionary<string, TBloomFilter> bloomFilters;

        /// <summary>
        /// 访问计数
        /// </summary>
        private Dictionary<string, int> visitCount;

        /// <summary>
        /// 过滤总数限制（0为不限制）
        /// </summary>
        private uint maxFilterCount;

        /// <summary>
        /// 基础路径
        /// </summary>
        private string basePath;

        /// <summary>
        /// 过滤器大小(字节)
        /// </summary>
        private int filterSize;

        /// <summary>
        /// 键数量
        /// </summary>
        protected int nKey;

        /// <summary>
        /// 扩展名
        /// </summary>
        private const string _extname = ".bf";

        #endregion

        /// <summary>
        /// 管理器初始化
        /// </summary>
        /// <param name="filterSize">过滤器大小(byte)</param>
        /// <param name="nKey"></param>
        /// <param name="basePath">基础路径</param>
        /// <param name="maxFilterCount">过滤器数量限制(缺省0)，用于控制内存总消耗量</param>
        public BloomFilterManager(int filterSize, int nKey, string basePath, uint maxFilterCount = 0)
        {
            this.filterSize = filterSize;
            this.nKey = nKey;
            this.basePath = basePath +(basePath.EndsWith(@"\") ? "" : @"\");
            this.maxFilterCount = maxFilterCount;


            //初始化字典及计数
            bloomFiltersLock = new object();
            bloomFilters = new Dictionary<string, TBloomFilter>();
            visitCount = new Dictionary<string, int>();

            //初始化缓存处理
            fileWriteBuffer = new byte[filterSize];
            fileReadBuffer = new byte[filterSize];
            writeFileLock = new object();
            readFileLock = new object();
        }

        /// <summary>
        /// 初始化
        /// initby folder，有异常可能
        /// 判断是否存在该文件夹
        /// 读取文件夹下文件
        /// 依次以二进制方式加载文件至指定数量
        /// 加载完成
        /// </summary>
        /// <param name="errMsg">out：错误信息</param>
        /// <param name="IsCreate">如果文件夹不存在是否新建</param>
        /// <returns></returns>
        /// <exception cref="">文件及文件夹部分可能会触发异常</exception>
        public bool Init(out string errMsg, bool IsCreate = false)
        {
            errMsg = string.Empty;

           

            if (!Directory.Exists(basePath))
            {
                if (IsCreate)
                {
                    Directory.CreateDirectory(basePath);
                }
                else
                {
                    errMsg = "基础文件夹不存在。";
                    return false;
                }
            }

            string[] fileList = Directory.GetFiles(basePath, "*" + _extname);

            uint fileCount = 0;
            if (maxFilterCount > 0)
            {
                fileCount = Math.Min(maxFilterCount, (uint)fileList.Length);
            }
            else
            {
                fileCount = (uint)fileList.Length;
            }

            if (fileCount > 0)
            {
                lock (bloomFiltersLock)
                {
                    for (int i = 0; i < fileCount; i++)
                    {
                        addFilter(fileList[i]);
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 增加信息
        /// </summary>
        /// <param name="val">过滤值接口</param>
        /// <returns>是否增加成功，不成功意味着值无效或者已有该值</returns>
        public bool Add(IBloomFilterData<ValueType> val)
        {
            bool rslt = false;

            if (val != null && val.IsValid())
            {
                TBloomFilter myFilter = default(TBloomFilter);
                lock (bloomFiltersLock)
                {
                    //判断组中是否有对应键值
                    if (!bloomFilters.ContainsKey(val.KeyName))
                    {
                        //没有相应键值，增加对应过滤器
                        addFilter(val.KeyName);
                    }

                    myFilter = bloomFilters[val.KeyName];

                    int keyCount = 0;
                    if (visitCount.TryGetValue(val.KeyName, out keyCount))
                    {
                        visitCount[val.KeyName] = keyCount + 1;
                    }
                    else
                    {
                        visitCount.Add(val.KeyName, keyCount + 1);
                    }
                        
                    //返回结果
                    rslt = AddValue(myFilter, val.Value);
                }
            }

            return rslt;
        }

        /// <summary>
        /// 保存全部过滤器
        /// </summary>
        public void SaveFilters()
        {
            lock (bloomFiltersLock)
            {
                foreach (KeyValuePair<string, TBloomFilter> myItem in bloomFilters)
                {
                    saveFilterToFile(myItem.Key, myItem.Value);
                }
            }
        }


        /// <summary>
        /// 是否含有该信息
        /// </summary>
        /// <param name="val">过滤值接口</param>
        /// <returns></returns>
        public bool IsHave(IBloomFilterData<ValueType> val)
        {
            bool rslt = false;

            if (val != null && val.IsValid())
            {
                TBloomFilter myFilter = default(TBloomFilter);
                lock (bloomFiltersLock)
                {
                    //判断组中是否有对应键值
                    if (!bloomFilters.ContainsKey(val.KeyName))
                    {
                        //没有相应键值，增加对应过滤器
                        addFilter(val.KeyName);
                    }

                    myFilter = bloomFilters[val.KeyName];


                    int keyCount = 0;
                    if (visitCount.TryGetValue(val.KeyName, out keyCount))
                    {
                        visitCount[val.KeyName] = keyCount + 1;
                    }
                    else
                    {
                        visitCount.Add(val.KeyName, keyCount + 1);
                    }
                }
                //返回结果
                rslt = IsHaveValue(myFilter, val.Value);
            }

            return rslt;
        }

        /// <summary>
        /// 手动调用GC
        /// </summary>
        public void Dispose()
        {
            fileReadBuffer = null;
            fileWriteBuffer = null;
            if (visitCount != null)
            {
                visitCount.Clear();
                visitCount = null;
            }
            if (bloomFilters != null)
            {
                foreach (KeyValuePair<string, TBloomFilter> myItem in bloomFilters)
                {
                    FilterDispose(myItem.Value);
                }
                bloomFilters.Clear();
                bloomFilters = null;
            }
            GC.Collect();
        }

        #region 私有部分

        /// <summary>
        /// 新增过滤器,计数器到字典
        /// </summary>
        /// <param name="key"></param>
        /// <param name="bloomFilter"></param>
        private void addFilter(string dicKey)
        {
            //处理是否需要进行控制数量
            if (maxFilterCount > 0)
            {
                controlFilterCount();
            }

            TBloomFilter bloomFilter = loadFilterFromFile(dicKey);

            if (!bloomFilters.ContainsKey(dicKey))
            {
                bloomFilters.Add(dicKey, bloomFilter);

            }

            if (!visitCount.ContainsKey(dicKey))
            {
                visitCount.Add(dicKey, 0);
            }

        }
        

        /// <summary>
        /// 根据字典关键字，加载过滤器
        /// 1、如果关键字所指文件存在，读取加载文件
        /// 2、如果关键字所指文件不存在，新建过滤器
        /// </summary>
        /// <param name="dicKey"></param>
        /// <returns></returns>
        private TBloomFilter loadFilterFromFile(string dicKey)
        {
            TBloomFilter newFilter = default(TBloomFilter);

            if (!File.Exists(basePath + dicKey + _extname))
            {
                //不存在该文件，新建缓存。
                newFilter = GetNewFilter(filterSize, nKey);
            }
            else
            {
                lock (writeFileLock)
                {
                    //清空读取缓存
                    for (int i = 0; i < filterSize; i++)
                    {
                        fileReadBuffer[i] = 0x0;
                    }
                    try
                    {
                        //存在该文件
                        FileStream myBinFile = new FileStream(basePath + dicKey + _extname, FileMode.Open, FileAccess.ReadWrite);
                        myBinFile.Read(fileReadBuffer, 0, filterSize);
                        myBinFile.Close();
                    }
                    catch (Exception ex)
                    {
                        //这里添加日志
                    }
                    newFilter = GetNewFilter(fileReadBuffer);
                }
            }

            return newFilter;
        }



        /// <summary>
        /// 保存单一内容
        /// </summary>
        /// <param name="dicKey"></param>
        private void saveFilterToFile(string dicKey)
        {
            TBloomFilter myFilter = default(TBloomFilter);
            if (bloomFilters.TryGetValue(dicKey, out myFilter))
            {
                saveFilterToFile(dicKey, myFilter);
            }
        }

        /// <summary>
        /// 保存单一内容
        /// </summary>
        /// <param name="dicKey"></param>
        private void saveFilterToFile(string dicKey, TBloomFilter myFilter)
        {
            //由于公用文件存储缓存，同一时间仅处理一个文件存储
            lock (writeFileLock)
            {
                CopyToBuffer(myFilter, ref fileWriteBuffer);
                try
                {
                    FileStream myBinFile = new FileStream(basePath + dicKey + _extname, FileMode.Create, FileAccess.Write);
                    myBinFile.Write(fileWriteBuffer, 0, filterSize);
                    myBinFile.Flush();
                    myBinFile.Close();
                }
                catch (Exception ex)
                {
                    //这里添加日志
                }
            }
        }

        #region 内存数量控制方式

        /// <summary>
        /// 控制过滤器数量
        /// </summary>
        private void controlFilterCount()
        {
            string curKeyName = string.Empty;
            int curCount = int.MaxValue;

            //独占计数处理部分，这期间不允许对计数列表进行变动
            //获取当前使用次数最少的
            foreach (KeyValuePair<string, int> myItem in visitCount)
            {
                if (myItem.Value < curCount)
                {
                    curCount = myItem.Value;
                    curKeyName = myItem.Key;
                }
            }

            //删除计数
            visitCount.Remove(curKeyName);
            //保存至文件
            saveFilterToFile(curKeyName);//查
            //删除
            bloomFilters.Remove(curKeyName);//删

        }
        

        #endregion


        #endregion


        #region 重载部分

        protected abstract void CopyToBuffer(TBloomFilter myFilter, ref byte[] myBuffer);

        protected abstract TBloomFilter GetNewFilter(byte[] myBuffer);

        protected abstract TBloomFilter GetNewFilter(int byteSize, int nKeys);

        protected abstract bool IsHaveValue(TBloomFilter bFilter, ValueType val);

        protected abstract bool AddValue(TBloomFilter bFilter, ValueType val);

        protected abstract void FilterDispose(TBloomFilter bFilter);

        #endregion
    }
}
